﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Text;

namespace ICalendar
{
    public static class Extensions
    {
        public const string UniversalDateTimeString = "yyyyMMddTHHmmssZ";
        public const string DateTimeString = "yyyyMMddTHHmmss";
        public const string DateString = "yyyyMMdd";

    //ROLE EXTENSION
        public static string GetName(this Role role)
        {
            switch (role)
            {
                case Role.Chair:
                    return "CHAIR";
                case Role.Required:
                    return "REQ-PARTICIPANT";
                case Role.Optional:
                    return "OPT-PARTICIPANT";
                case Role.None:
                    return "NON-PARTICIPANT";
                default:
                    throw new ArgumentOutOfRangeException("role");
            }
        }
        

        public static void FromName(this Role roleclass, string role)
        {
            if (string.IsNullOrEmpty(role))
            {
                roleclass = Role.None;
                return;
            }

            switch (role.Trim().ToUpperInvariant())
            {
                case "CHAIR":
                    roleclass = Role.Chair;
                    break;
                case "REQ-PARTICIPANT":
                    roleclass = Role.Required;
                    break;
                case "OPT-PARTICIPANT":
                    roleclass = Role.Optional;
                    break;
                case "NON-PARTICIPANT":
                    roleclass = Role.None;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("role");
            }
        }


        public static string ToString(this TodoStatusValue? todo)
        {
            switch (todo)
            {
                case TodoStatusValue.NeedsAction:
                    return "NEEDS-ACTION";
                case TodoStatusValue.Completed:
                    return "COMPLETED";
                case TodoStatusValue.InProcess:
                    return "IN-PROCESS";
                case TodoStatusValue.Cancelled:
                    return "CANCELLED";
                default:
                    return null;
            }
        }
        
        public static void FromName(this TodoStatusValue? todoClass, string todo)
        {
            if (string.IsNullOrEmpty(todo))
            {
                todoClass = null;
                return;
            }

            switch (todo.Trim().ToUpperInvariant())
            {
                case "NEEDS-ACTION":
                    todoClass = TodoStatusValue.NeedsAction;
                    return;
                case "COMPLETED":
                    todoClass = TodoStatusValue.Completed;
                    return;
                case "IN-PROCESS":
                    todoClass = TodoStatusValue.InProcess;
                    return;
                case "CANCELLED":
                    todoClass = TodoStatusValue.Cancelled;
                    return;
                default:
                    todoClass = null;
                    break;
            }
        }


        //Classification Extension
        public static string GetName(this ClassificationValue value)
        {
            switch (value)
            {
                case ClassificationValue.Public:
                    return "PUBLIC";
                case ClassificationValue.Private:
                    return "PRIVATE";
                case ClassificationValue.Confidential:
                    return "CONFITENCIAL";
                default:
                    throw new ArgumentOutOfRangeException("value");
            }
        }
        
        public static void FromName(this ClassificationValue classValue, string classification)
        {
            if (string.IsNullOrEmpty(classification))
            {
                classValue = ClassificationValue.Public;
                return;
            }

            switch (classification.Trim().ToUpperInvariant())
            {
                case "PUBLIC":
                    classValue = ClassificationValue.Public;
                    break;
                case "PRIVATE":  
                    classValue = ClassificationValue.Private;
                    break;
                case "CONFIDENTIAL":
                    classValue = ClassificationValue.Confidential;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("classValue");
            }
        }

        ///Method Extension
        public static string GetName(this Method method)
        {
            switch (method)
            {
                case Method.Publish:
                    return MethodNames.Publish;
                case Method.Request:
                    return MethodNames.Request;
                case Method.Reply:
                    return MethodNames.Reply;
                case Method.Add:
                    return MethodNames.Add;
                case Method.Cancel:
                    return MethodNames.Add;
                case Method.Refresh:
                    return MethodNames.Refresh;
                case Method.Counter:
                    return MethodNames.Counter;
                case Method.DeclineCounter:
                    return MethodNames.DeclineCounter;
                default:
                    throw new ArgumentOutOfRangeException("method");
            }         
        }

        public static void FromName(this Method methodClass, string method)
        {
            if (string.IsNullOrEmpty(method))
            {
                methodClass = Method.Publish;
                return;
            }

            switch (method.Trim().ToUpperInvariant())
            {
                case MethodNames.Publish:
                    methodClass = Method.Publish;
                    break;
                case MethodNames.Request:
                    methodClass = Method.Request;
                    break;
                case MethodNames.Reply:
                    methodClass = Method.Reply;
                    break;
                case MethodNames.Add:
                    methodClass = Method.Add;
                    break;
                case MethodNames.Cancel:
                    methodClass = Method.Add;
                    break;
                case MethodNames.Refresh:
                    methodClass = Method.Refresh;
                    break;
                case MethodNames.Counter:
                    methodClass = Method.Counter;
                    break;
                case MethodNames.DeclineCounter:
                    methodClass = Method.DeclineCounter;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("method");
            }
        }


        ///ValueType Extension
        public static string GetName(this ValueType valueType)
        {
            switch (valueType)
            {
                case ValueType.Binary:
                    return ValueTypeNames.Binary;
                case ValueType.Boolean:
                    return ValueTypeNames.Boolean;
                case ValueType.CalAddress:
                    return ValueTypeNames.CalAddress;
                case ValueType.Date:
                    return ValueTypeNames.Date;
                case ValueType.DateTime:
                    return ValueTypeNames.DateTime;
                case ValueType.Duration:
                    return ValueTypeNames.Duration;
                case ValueType.Float:
                    return ValueTypeNames.Float;
                case ValueType.Integer:
                    return ValueTypeNames.Integer;
                case ValueType.Period:
                    return ValueTypeNames.Period;
                case ValueType.Recur:
                    return ValueTypeNames.Recur;
                case ValueType.Text:
                    return ValueTypeNames.Text;
                case ValueType.Time:
                    return ValueTypeNames.Time;
                case ValueType.URI:
                    return ValueTypeNames.URI;
                case ValueType.UTCOffset:
                    return ValueTypeNames.UTCOffset;
                default:
                    throw new ArgumentOutOfRangeException("valueType");
            }
        }

        public static ValueType FromName(this ValueType valueTypeClass, string valueType)
        {
            if (string.IsNullOrEmpty(valueType))
            {
                return ValueType.Text;
            }

            switch (valueType.Trim().ToUpperInvariant())
            {
                case ValueTypeNames.Binary:
                    return ValueType.Binary;
                case ValueTypeNames.Boolean:
                    return ValueType.Boolean;
                case ValueTypeNames.CalAddress:
                    return ValueType.CalAddress;
                case ValueTypeNames.Date:
                    return ValueType.Date;
                case ValueTypeNames.DateTime:
                    return ValueType.DateTime;
                case ValueTypeNames.Duration:
                    return ValueType.Duration;
                case ValueTypeNames.Float:
                    return ValueType.Float;
                case ValueTypeNames.Integer:
                    return ValueType.Integer;
                case ValueTypeNames.Period:
                    return ValueType.Period;
                case ValueTypeNames.Recur:
                    return ValueType.Recur;
                case ValueTypeNames.Text:
                    return ValueType.Text;
                case ValueTypeNames.Time:
                    return ValueType.Time;
                case ValueTypeNames.URI:
                    return ValueType.URI;
                case ValueTypeNames.UTCOffset:
                    return ValueType.UTCOffset;
                default:
                    throw new ArgumentOutOfRangeException("valueType");
            }
        }

        //ParticipationStatus Extensions
        public static ParticipationStatus FromParticipationStatusName(this ParticipationStatus participationStatus, string status)
        {
            if (string.IsNullOrEmpty(status))
            {
                return ParticipationStatus.NeedsAction;
            }

            switch (status.Trim().ToUpperInvariant())
            {
                //DEFAULT
                case "NEEDS-ACTION":
                    return ParticipationStatus.NeedsAction;
                case "ACCEPTED":
                    return ParticipationStatus.Accepted;
                case "DECLINED":
                    return ParticipationStatus.Declined;
                case "TENTATIVE":
                    return ParticipationStatus.Tentative;
                case "DELEGATED":
                    return ParticipationStatus.Delegated;
                case "COMPLETED":
                    return ParticipationStatus.Completed;
                case "IN-PROCESS":
                    return ParticipationStatus.InProcess;
                default:
                    return ParticipationStatus.NeedsAction;
            }
        }

        public static string GetParticipationStatusName(this ParticipationStatus status)
        {
            switch (status)
            {
                case ParticipationStatus.NeedsAction:
                    return "NEEDS-ACTION";
                case ParticipationStatus.Accepted:
                    return "ACCEPTED";
                case ParticipationStatus.Declined:
                    return "DECLINED";
                case ParticipationStatus.Tentative:
                    return "TENTATIVE";
                case ParticipationStatus.Delegated:
                    return "DELEGATED";
                case ParticipationStatus.Completed:
                    return "COMPLETED";
                case ParticipationStatus.InProcess:
                    return "IN-PROCESS";
                default:
                    throw new ArgumentOutOfRangeException("status");
            }
        }
        


        //MailAddressCollection Extensions
        public static MailAddressCollection FromCalAddressCollectionString(this MailAddressCollection address, string members)
        {
            if (string.IsNullOrEmpty(members))
            {
                return null;
            }

            string[] memberList = members.Split(',');

            var result = new MailAddressCollection();

            foreach (var s in memberList)
            {
                result.Add(s.Replace("\"", ""));
            }

            return result;
        }
        public static string GetCalAddressCollectionString(this MailAddressCollection members)
        {
            if (members == null || members.Count <= 0)
            {
                return null;
            }

            var result = new StringBuilder();

            for (int i = 0; i < members.Count; i++)
            {
                result.AppendFormat(",\"{0}\"", GetCalAddress(members[i]));
            }

            result.Remove(0, 1);
            return result.ToString();
        }
        
        public static string GetCalAddress(this MailAddress address)
        {
            return address != null ? string.Format("mailto:{0}", address.Address) : null;
        }

        public static MailAddress FromCalAddress(this string address)
        {
            return address != null ? new MailAddress(address.Replace("mailto:", "")) : null;
        }

        public static MailAddress FromCommonName(this MailAddress address, string commonName)
        {
            if (address == null)
                return null;

            return new MailAddress(address.Address, commonName != null ? commonName.Replace("\"", "") : null);
        }

        public static string GetCommonName(this MailAddress address)
        {
            return address.DisplayName != null ? String.Format("\"{0}\"", address.DisplayName) : address.ToString();
        }


        //STRING EXTENSIONS
        // Escape text as needed for compliance with RFC 2445 (Sec 4.3.11)
        public static string Escape(this string text)
        {
            if (string.IsNullOrEmpty(text))
                return null;

            text = text.Replace(@"\", "\\\\");
            text = text.Replace(";", "\\;");
            text = text.Replace(",", "\\,");
            text = text.Replace("\n", "\\n");
            return text;
        }

        public static string UnEscape(this string text)
        {
            if (string.IsNullOrEmpty(text))
                return null;

            text = text.Replace("\\\\", @"\");
            text = text.Replace("\\;", ";");
            text = text.Replace("\\,", ",");
            text = text.Replace("\\n", "\n");
            return text;
        }
        /// <summary>
        ///    Fold the line after every 70 chars (RFC 2445 Sec 4.1)
        /// </summary>
        /// <remarks>
        /// The iCalendar object is organized into individual lines of text,
        ///called content lines. Content lines are delimited by a line break,
        ///which is a CRLF sequence (US-ASCII decimal 13, followed by US-ASCII
        ///decimal 10).
        /// 
        ///Lines of text SHOULD NOT be longer than 75 octets, excluding the line
        ///break. Long content lines SHOULD be split into a multiple line
        ///representations using a line "folding" technique. That is, a long
        ///line can be split between any two characters by inserting a CRLF
        ///immediately followed by a single linear white space character (i.e.,
        ///SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
        ///of CRLF followed immediately by a single linear white space character
        ///is ignored (i.e., removed) when processing the content type.
        /// 
        ///For example the line:
        /// 
        ///  DESCRIPTION:This is a long description that exists on a long line.
        /// 
        ///Can be represented as:
        /// 
        ///  DESCRIPTION:This is a lo
        ///   ng description
        ///    that exists on a long line.
        ///The process of moving from this folded multiple line representation
        ///to its single line representation is called "unfolding". Unfolding is
        ///accomplished by removing the CRLF character and the linear white
        ///space character that immediately follows.
        ///When parsing a content line, folded lines MUST first be unfolded
        ///according to the unfolding procedure described above. When generating
        ///a content line, lines longer than 75 octets SHOULD be folded
        ///according to the folding procedure described above.
        /// </remarks>
        /// <param name="line"></param>
        public static string Folding(this string line)
        {
            // Fold the line after every 70 chars (RFC 2445 Sec 4.1)
            var positions = new List<int>();
            for (var i = 69; i < line.Length; i = i + 69)
            {
                positions.Add(i);
            }
            foreach (var i in positions)
            {
                line = line.Insert(i, "\r\n ");
            }
            return line;
        }
        public static string UnFolding(this string line)
        {
            line = line.Replace("\r\n ", "");
            line = line.Replace("\r\n\t", "");
            return line;
        }

        ////EXTENSIONS DATETIME
        //public static void FromICalendarUniversalTimeString(this DateTime dateTime, string value)
        //{            
            
        //    return dateTime.ToUniversalTime().ToString(UniversalDateTimeString);
        //}

        //public static void FromICalendarDateTimeString(this DateTime dateTime)
        //{
        //    return dateTime.ToString(DateTimeString);
        //}
        public static string ToICalendarUniversalTimeString(this DateTime dateTime)
        {
            return dateTime.ToUniversalTime().ToString(UniversalDateTimeString);
        }

        public static string ToICalendarDateTimeString(this DateTime dateTime)
        {
            return dateTime.ToString(DateTimeString);
        }

        public static string ToICalendarDateString(this DateTime dateTime)
        {
            return dateTime.ToString(DateString);
        }
    }
}



//    using System;
//using System.Collections.Generic;
//using System.Text;
//using System.Linq;

//namespace EnumExtension
//{
//    // Define an extension method in a non-nested static class.
//    public static class Extensions
//    {        
//        public static Grades minPassing = Grades.D;
//        public static bool Passing(this Grades grade)
//        {
//            return grade >= minPassing;
//        }
//    }

//    public enum Grades { F = 0, D=1, C=2, B=3, A=4 };
//    class Program
//    {       
//        static void Main(string[] args)
//        {
//            Grades g1 = Grades.D;
//            Grades g2 = Grades.F;
//            Console.WriteLine("First {0} a passing grade.", g1.Passing() ? "is" : "is not");
//            Console.WriteLine("Second {0} a passing grade.", g2.Passing() ? "is" : "is not");

//            Extensions.minPassing = Grades.C;
//            Console.WriteLine("\r\nRaising the bar!\r\n");
//            Console.WriteLine("First {0} a passing grade.", g1.Passing() ? "is" : "is not");
//            Console.WriteLine("Second {0} a passing grade.", g2.Passing() ? "is" : "is not");
//        }
//    }
//  }
//}
///* Output:
//    First is a passing grade.
//    Second is not a passing grade.

//    Raising the bar!

//    First is not a passing grade.
//    Second is not a passing grade.
// */